
#region Imports

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;

using Justa.Reflection;

#endregion

namespace Justa.Aspect
{
    /// <summary>
    /// 
    /// </summary>
    /// 
    /// <author>Abel Perez</author>
    /// 
    public class AspectInvoker : DynamicProxy
    {
        /// <summary>
        /// 
        /// </summary>
        /// 
        public event BeforeAdvice before;

        /// <summary>
        /// 
        /// </summary>
        /// 
        public event AfterAdvice after;

        /// <summary>
        /// 
        /// </summary>
        /// 
        public event AroundAdvice invoker;

        /// <summary>
        /// 
        /// </summary>
        /// 
        public event AfterThrowsAdvice throws;

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="target"></param>
        /// <param name="classToProxy"></param>
        /// 
        public AspectInvoker(object target, Type classToProxy) 
            : base(target, classToProxy)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// 
        /// <param name="msg"></param>
        /// 
        /// <returns></returns>
        /// 
        public override IMessage Invoke(IMessage msg)
        {
            IMethodCallMessage callMessage = (IMethodCallMessage)msg;
            MethodInfo methodInfo = callMessage.MethodBase as MethodInfo;

            if (before != null)
            {
                before();
            }

            JoinPoint point = new JoinPoint(target, this, methodInfo, callMessage.Args);

            object returnValue = null;

            try
            {
                if (invoker != null)
                {
                    returnValue = invoker(point);
                }
                else
                {
                    returnValue = callMessage.MethodBase.Invoke(target, callMessage.Args);
                }
            }
            catch (Exception exception)
            {
                if (throws != null)
                {
                    throws(exception);
                    throw exception;
                }
            }

            ReturnMessage result = new ReturnMessage(returnValue, new Object[] { }, 0, callMessage.LogicalCallContext, callMessage);

            if (after != null)
            {
                after(returnValue);
            }

            return result;
        }
    }
}